/*
 * This file is part of the GWTUML project and was written by Mounier Florian <mounier-dot-florian.at.gmail'dot'com> for Objet Direct
 * <http://wwww.objetdirect.com>
 * 
 * Copyright © 2009 Objet Direct Contact: gwtuml@googlegroups.com
 * 
 * GWTUML is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * GWTUML is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with GWTUML. If not, see <http://www.gnu.org/licenses/>.
 */
package com.objetdirect.gwt.umlapi.client.gfx;

import com.objetdirect.gwt.umlapi.client.emul.Log;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Widget;
import com.objetdirect.gwt.umlapi.client.engine.Point;
import com.objetdirect.gwt.umlapi.client.helpers.GWTUMLDrawerHelper;
import com.objetdirect.gwt.umlapi.client.emul.def.ImageGfx;
import com.objetdirect.gwt.umlapi.client.emul.impl.TatamiImageGfx;
import com.objetdirect.gwt.umlapi.client.emul.def.Line;
import com.objetdirect.gwt.umlapi.client.emul.impl.TatamiLine;
import com.objetdirect.gwt.umlapi.client.emul.def.Circle;
import com.objetdirect.gwt.umlapi.client.emul.def.GraphicCanvas;
import com.objetdirect.gwt.umlapi.client.emul.def.GraphicObject;
import com.objetdirect.gwt.umlapi.client.emul.def.GraphicObjectListener;
import com.objetdirect.gwt.umlapi.client.emul.impl.TatamaiCircle;
import com.objetdirect.gwt.umlapi.client.emul.def.Path;
import com.objetdirect.gwt.umlapi.client.emul.impl.TatamiPath;
import com.objetdirect.gwt.umlapi.client.emul.def.Color;
import com.objetdirect.gwt.umlapi.client.emul.impl.TatamiColor;
import com.objetdirect.gwt.umlapi.client.emul.def.Font;
import com.objetdirect.gwt.umlapi.client.emul.impl.TatamiFont;
import com.objetdirect.gwt.umlapi.client.emul.impl.TatamaiGraphicObject;
import com.objetdirect.gwt.umlapi.client.emul.impl.TatamaiGraphicObjectListener;
import com.objetdirect.gwt.umlapi.client.emul.impl.TatamiGraphicCanvas;
import com.objetdirect.gwt.umlapi.client.emul.impl.TatamiPoint;
import com.objetdirect.gwt.umlapi.client.emul.def.Rect;
import com.objetdirect.gwt.umlapi.client.emul.impl.TatamiRect;
import com.objetdirect.gwt.umlapi.client.emul.def.Text;
import com.objetdirect.gwt.umlapi.client.emul.impl.TatamiText;
import com.objetdirect.gwt.umlapi.client.emul.def.VirtualGroup;
import com.objetdirect.gwt.umlapi.client.emul.impl.TatamiVirtualGroup;

/**
 * This class implements the graphic platform using the <a
 * href="http://code.google.com/p/tatami/">Tatami</a> graphics library
 * 
 * @author Florian Mounier (mounier-dot-florian.at.gmail'dot'com)
 */
public class TatamiGfxPlatform implements GfxPlatform {

	@Override
	public void addObjectListenerToCanvas(final Widget canvas,
			final GfxObjectListener gfxObjectListener) {

		final GraphicObjectListener graphicObjectListener = new TatamaiGraphicObjectListener(
				gfxObjectListener);
		/*
		 * final GraphicObjectListener graphicObjectListener = new
		 * GraphicObjectListener() {
		 * 
		 * @Override public void mouseClicked(final GraphicObject graphicObject,
		 * final Event e) { // This app doesn't need click event }
		 * 
		 * 
		 * @Override public void mouseDblClicked(final GraphicObject
		 * graphicObject, final Event e) {
		 * gfxObjectListener.mouseDoubleClicked(TatamiGfxObjectContainer
		 * .getContainerOf(graphicObject), e); }
		 * 
		 * 
		 * @Override public void mouseMoved(final GraphicObject graphicObject,
		 * final Event e) { gfxObjectListener.mouseMoved(e); }
		 * 
		 * 
		 * @Override public void mousePressed(final GraphicObject graphicObject,
		 * final Event e) {
		 * gfxObjectListener.mousePressed(TatamiGfxObjectContainer
		 * .getContainerOf(graphicObject), e); }
		 * 
		 * 
		 * @Override public void mouseReleased(final GraphicObject
		 * graphicObject, final Event e) {
		 * gfxObjectListener.mouseReleased(TatamiGfxObjectContainer
		 * .getContainerOf(graphicObject), e); }
		 * 
		 * 
		 * @Override public Object getInnerObject() { // TODO Auto-generated
		 * method stub return null; } };
		 */
		GraphicCanvas aCanvas = new TatamiGraphicCanvas(canvas);
		aCanvas.addGraphicObjectListener(graphicObjectListener);
	}

	@Override
	public void addToCanvas(final Widget canvas, final GfxObject gfxO,
			final Point location) {
		Log.trace("Adding to Tcanvas : "
				+ GWTUMLDrawerHelper.getShortName(this
						.getTatamiGraphicalObjectFrom(gfxO)));
		GraphicCanvas aCanvas = new TatamiGraphicCanvas(canvas);
		aCanvas.add(this.getTatamiGraphicalObjectFrom(gfxO), location.getX(),
				location.getY());
	}

	@Override
	public void addToVirtualGroup(final GfxObject gfxOGroup,
			final GfxObject gfxO) {
		Log.trace("Adding " + gfxO + " to " + gfxOGroup);
		GraphicObject aGraphicalObject = this
				.getTatamiGraphicalObjectFrom(gfxOGroup);
		VirtualGroup aVirtualGroup = new TatamiVirtualGroup(aGraphicalObject);
		
		aVirtualGroup.add(this
				.getTatamiGraphicalObjectFrom(gfxO));
	}

	@Override
	public GfxObject buildCircle(final int radius) {
		Circle circle = new TatamaiCircle(radius);
		TatamaiGraphicObject aTatamaiGraphicObject = new TatamaiGraphicObject(
				circle);
		return new TatamiGfxObjectContainer(aTatamaiGraphicObject);
	}

	@Override
	public GfxObject buildImage(final String url) {
		ImageGfx anImageGfx = new TatamiImageGfx(new Image(url));
		TatamaiGraphicObject aTatamaiGraphicObject = new TatamaiGraphicObject(
				anImageGfx);
		return new TatamiGfxObjectContainer(aTatamaiGraphicObject);
	}

	@Override
	public GfxObject buildLine(final Point p1, final Point p2) {

		Line tatamiLine = new TatamiLine(p1.getX(), p1.getY(), p2.getX(),
				p2.getY());
		TatamaiGraphicObject aTatamaiGraphicObject = new TatamaiGraphicObject(
				tatamiLine);
		return new TatamiGfxObjectContainer(aTatamaiGraphicObject);
	}

	@Override
	public GfxObject buildPath() {
		TatamaiGraphicObject aTatamaiGraphicObject = new TatamaiGraphicObject(
				new TatamiPath());
		return new TatamiGfxObjectContainer(aTatamaiGraphicObject);
	}

	@Override
	public GfxObject buildRect(final int width, final int height) {
		Rect tatamiRect = new TatamiRect(width, height);
		TatamaiGraphicObject aTatamaiGraphicObject = new TatamaiGraphicObject(
				tatamiRect);
		return new TatamiGfxObjectContainer(aTatamaiGraphicObject);
	}

	@Override
	public GfxObject buildText(final String text, final Point initLocation) {
		final Text txt = new TatamiText(text);
		txt.translate(initLocation.getX(),
				initLocation.getY() + (int) ((txt.getHeight() * 64.) / 100.));
		TatamaiGraphicObject aTatamaiGraphicObject = new TatamaiGraphicObject(
				txt);
		return new TatamiGfxObjectContainer(aTatamaiGraphicObject);
	}

	@Override
	public GfxObject buildVirtualGroup() {
		VirtualGroup tatamiVirtualGroup = new TatamiVirtualGroup();
		TatamaiGraphicObject aTatamaiGraphicObject = new TatamaiGraphicObject(
				tatamiVirtualGroup);
		final GfxObject vg = new TatamiGfxObjectContainer(aTatamaiGraphicObject);
		Log.trace("Creating " + vg);
		return vg;
	}

	@Override
	public void clearVirtualGroup(final GfxObject gfxOGroup) {
		GraphicObject aGraphicalObject = this
				.getTatamiGraphicalObjectFrom(gfxOGroup);
		VirtualGroup aVirtualGroup = new TatamiVirtualGroup(aGraphicalObject);
		aVirtualGroup.clear();
	}

	@Override
	public void curveTo(final GfxObject gfxO, final Point location,
			final Point control) {
		GraphicObject aGraphicalObject = this
				.getTatamiGraphicalObjectFrom(gfxO);
		Path path = new TatamiPath(aGraphicalObject);
		path.qCurveTo(
				this.pointConverter(control), this.pointConverter(location));
	}

	@Override
	public void curveTo(final GfxObject gfxO, final Point location,
			final Point control1, final Point control2) {
		GraphicObject aGraphicalObject = this
				.getTatamiGraphicalObjectFrom(gfxO);
		Path path = new TatamiPath(aGraphicalObject);
		path.curveTo(
				this.pointConverter(control1), this.pointConverter(control2),
				this.pointConverter(location));
	}

	@Override
	public GfxObject getGroup(final GfxObject gfxO) {
		return TatamiGfxObjectContainer.getContainerOf(this
				.getTatamiGraphicalObjectFrom(gfxO).getGroup());

	}

	@Override
	public Point getLocationFor(final GfxObject gfxO) {
		if (gfxO != null) {
			if (this.getTatamiGraphicalObjectFrom(gfxO).getClass()
					.equals(Text.class)) {
				GraphicObject aGraphicalObject = this
						.getTatamiGraphicalObjectFrom(gfxO);
				final Text txt = new TatamiText(aGraphicalObject);
 
				return new Point(txt.getX(), txt.getY()
						- (int) ((txt.getHeight() * 64.) / 100.));
			}
			return new Point(this.getTatamiGraphicalObjectFrom(gfxO).getX(),
					this.getTatamiGraphicalObjectFrom(gfxO).getY());
		}
		return Point.getOrigin();
	}

	@Override
	public int getTextHeightFor(final GfxObject gfxO) {
		if (gfxO != null) {
			GraphicObject aGraphicalObject = this
					.getTatamiGraphicalObjectFrom(gfxO);
			final Text txt = new TatamiText(aGraphicalObject);
			return (int) ((txt.getHeight() * 8.) / 10.); // Converting
												// point to
												// pixel
		}
		return 0;
	}

	@Override
	public int getTextWidthFor(final GfxObject gfxO) {
		if (gfxO != null) {
			GraphicObject aGraphicalObject = this
					.getTatamiGraphicalObjectFrom(gfxO);
			final Text txt = new TatamiText(aGraphicalObject);
			return (int) ((txt.getWidth() * 8.) / 10.); // Converting point
												// to pixel
		}
		return 0;
	}

	@Override
	public void lineTo(final GfxObject gfxO, final Point location) {
		GraphicObject aGraphicalObject = this
				.getTatamiGraphicalObjectFrom(gfxO);
		Path path = new TatamiPath(aGraphicalObject);
		path.lineTo(location.getX(), location.getY());
	}

	@Override
	public Widget makeCanvas() {
		return this.makeCanvas(GfxPlatform.DEFAULT_CANVAS_WIDTH,
				GfxPlatform.DEFAULT_CANVAS_HEIGHT, GfxColor.WHITE);
	}

	@Override
	public Widget makeCanvas(final int width, final int height,
			final GfxColor backgroundColor) {
		final GraphicCanvas canvas = new TatamiGraphicCanvas();
		canvas.setSize(width + "px", height + "px");
		DOM.setStyleAttribute(
				canvas.getElement(),
				"backgroundColor",
				new TatamiColor(backgroundColor.getRed(), backgroundColor
						.getGreen(), backgroundColor.getBlue()
				/*
				 * , backgroundColor.getAlpha() Disabled to ensure#@&~#! IE
				 * compatibility
				 */, 0).toHex());
		return (Widget) canvas.getInnerObject();
	}

	@Override
	public void moveTo(final GfxObject gfxO, final Point location) {
		GraphicObject aGraphicalObject = this
				.getTatamiGraphicalObjectFrom(gfxO);
		Path path = new TatamiPath(aGraphicalObject);
		path.moveTo(this.pointConverter(location));
	}

	@Override
	public void moveToBack(final GfxObject gfxO) {
		this.getTatamiGraphicalObjectFrom(gfxO).moveToBack();

	}

	@Override
	public void moveToFront(final GfxObject gfxO) {
		this.getTatamiGraphicalObjectFrom(gfxO).moveToFront();
	}

	@Override
	public void removeFromCanvas(final Widget canvas, final GfxObject gfxO) {
		Log.trace("Removing from Tcanvas : "
				+ GWTUMLDrawerHelper.getShortName(this
						.getTatamiGraphicalObjectFrom(gfxO)));
		GraphicCanvas aCanvas = new TatamiGraphicCanvas(canvas);
		aCanvas.remove(this.getTatamiGraphicalObjectFrom(gfxO));
	}

	@Override
	public void removeFromVirtualGroup(final GfxObject gfxOGroup,
			final GfxObject gfxO, final boolean isSilent) {
		GraphicObject aGraphicalObject = this
				.getTatamiGraphicalObjectFrom(gfxOGroup);
		VirtualGroup aVirtualGroup = new TatamiVirtualGroup(aGraphicalObject);
		
		aVirtualGroup.remove(
				this.getTatamiGraphicalObjectFrom(gfxO), isSilent);
	}

	@Override
	public void rotate(final GfxObject gfxO, final float angle,
			final Point center) {
		this.getTatamiGraphicalObjectFrom(gfxO).rotate(angle,
				this.pointConverter(center));
	}

	@Override
	public void setFillColor(final GfxObject gfxO, final GfxColor color) {
		this.getTatamiGraphicalObjectFrom(gfxO).setFillColor(
				this.convertColor(color));
	}

	@Override
	public void setFont(final GfxObject gfxO, final GfxFont gfxF) {
		GraphicObject aGraphicalObject = this
				.getTatamiGraphicalObjectFrom(gfxO);
		final Text txt = new TatamiText(aGraphicalObject);
		txt.setFont(this
				.convertFont(gfxF));
	}

	@Override
	public void setOpacity(final GfxObject gfxO, final int opacity,
			final boolean isForBack) {
		if (isForBack) {
			this.getTatamiGraphicalObjectFrom(gfxO).setOpacity(
					(int) (((double) (opacity * 100)) / 255));
		} else {

			final Color strokeColor = this.getTatamiGraphicalObjectFrom(gfxO)
					.getStrokeColor();
			this.getTatamiGraphicalObjectFrom(gfxO).setStrokeColor(
					new TatamiColor(strokeColor.getRed(), strokeColor
							.getGreen(), strokeColor.getBlue(), opacity));
		}
	}

	@Override
	public void setSize(final Widget canvas, final int width, final int height) {
		GraphicCanvas aCanvas = new TatamiGraphicCanvas(canvas);
		aCanvas.setDimensions(width, height);
	}

	@Override
	public void setStroke(final GfxObject gfxO, final GfxColor color,
			final int width) {
		if (this.getTatamiGraphicalObjectFrom(gfxO).getClass()
				.equals(Text.class)) {
			this.getTatamiGraphicalObjectFrom(gfxO)
					.setOpacity(color.getAlpha());
		}
		this.getTatamiGraphicalObjectFrom(gfxO).setStroke(
				this.convertColor(color), width);
	}

	@Override
	public void setStrokeStyle(final GfxObject gfxO, final GfxStyle style) {
		this.getTatamiGraphicalObjectFrom(gfxO).setStrokeStyle(
				style.getStyleString());
	}

	@Override
	public void translate(final GfxObject gfxO, final Point location) {
		this.getTatamiGraphicalObjectFrom(gfxO).translate(location.getX(),
				location.getY());
	}

	private Color convertColor(final GfxColor gfxColor) {
		Color color = new TatamiColor(gfxColor.getRed(), gfxColor.getGreen(),
				gfxColor.getBlue(), gfxColor.getAlpha());
		return color;
	}

	private Font convertFont(final GfxFont gfxFont) {

		Font font = new TatamiFont(gfxFont.getFamily(), gfxFont.getSize(),
				gfxFont.getStyle(), gfxFont.getVariant(), gfxFont.getWeight());
		return font;
	}

	private GraphicObject getTatamiGraphicalObjectFrom(final GfxObject gfxO) {
		return ((TatamiGfxObjectContainer) gfxO).getGraphicObject();
	}

	private com.objetdirect.gwt.umlapi.client.emul.def.Point pointConverter(
			final Point p) {
		return new TatamiPoint(p.getX(), p.getY());
	}

}